package com.ly.demo.bean;

import cn.hutool.extra.mail.MailAccount;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.Subscribe;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.ly.demo.annotation.EventBusListener;
import com.ly.demo.service.impl.RedisMessageSubscriber;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Author liuyang
 * @Date 2023/6/26 15:09
 **/
@Configuration
@Slf4j
public class MyBean {
    // 事件总线线程池
    private static final ThreadPoolExecutor EVENT_BUS_WORKER_POOL = new ThreadPoolExecutor(
            6, 10, 1, TimeUnit.MINUTES,
            new ArrayBlockingQueue<>(5000),
            new ThreadFactoryBuilder().setNameFormat("event_bus_thread_%d").build(),
            (r, executor) -> log.error("reject event bus job")
    );

    static {
        // 允许核心线程回收
        EVENT_BUS_WORKER_POOL.allowCoreThreadTimeOut(true);
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public MailAccount mailAccount() {
        MailAccount mailAccount = new MailAccount();
        mailAccount.setUser("1733493504");
        mailAccount.setPass("ikzzhzcfbzgtcfeg");
        mailAccount.setPort(465);
        mailAccount.setHost("smtp.qq.com");
        mailAccount.setAuth(true);
        mailAccount.setSslEnable(true);
        mailAccount.setFrom("1733493504@qq.com");
        return mailAccount;
    }
    public static void main(String[] args) {
        System.out.println(8&8);
    }
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
//
//    @Bean
//    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,
//                                                                       MessageListenerAdapter listenerAdapter) {
//        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
//        container.setConnectionFactory(connectionFactory);
//        container.addMessageListener(listenerAdapter, new PatternTopic("P1"));
//        return container;
//    }
//    @Bean
//    public MessageListenerAdapter listenerAdapter(RedisMessageSubscriber subscriber) {
//        return new MessageListenerAdapter(subscriber, "onMessage");
//    }


    @Bean
    public AsyncEventBus asyncEventBus(ApplicationContext applicationContext) throws Exception {
        AsyncEventBus asyncEventBus = new AsyncEventBus(EVENT_BUS_WORKER_POOL);
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(EventBusListener.class));
        Set<BeanDefinition> components = scanner.findCandidateComponents("com.ly.demo");
        for (BeanDefinition component : components) {
            String className = ((AnnotatedBeanDefinition) component).getMetadata().getClassName();
            log.info("[asyncEventBus] scan class:{}", className);
            Class<?> clazz = Class.forName(className);
            if (Arrays.stream(clazz.getDeclaredMethods()).anyMatch(v -> v.getAnnotation(Subscribe.class) != null)) {
                Object listener = clazz.newInstance();
                // 注入属性
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    Resource annotation = field.getAnnotation(Resource.class);
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    if (annotation != null || autowired != null) {
                        field.set(listener, Optional.of(applicationContext.getBean(field.getName())).orElse(null));
                    }
                }
                asyncEventBus.register(listener);
                log.info("[asyncEventBus] register listener:{}", className);
            }
        }
        return asyncEventBus;
    }

    /**
     * 所有对Redisson的使用都是通过RedissonClient
     * @return
     * @throws IOException
     */
//    @Bean(destroyMethod="shutdown")
//    public RedissonClient redissonClient() throws IOException {
//        //1、创建配置
//        Config config = new Config();
//        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
//
//        //2、根据Config创建出RedissonClient实例
//        //Redis url should start with redis:// or rediss://
//        RedissonClient redissonClient = Redisson.create(config);
//        return redissonClient;
//    }

    @Bean
    public <K, V> RedisTemplate<K, V> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<K, V> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        template.setHashValueSerializer(RedisSerializer.json());
        template.setValueSerializer(RedisSerializer.json());
        return template;
    }
}
